package com.bjy.qa.service.functionaltest.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bjy.qa.dao.functionaltest.*;
import com.bjy.qa.entity.MyPage;
import com.bjy.qa.entity.ResponsePagingData;
import com.bjy.qa.entity.functionaltest.*;
import com.bjy.qa.enumtype.AgentStatus;
import com.bjy.qa.enumtype.CatalogType;
import com.bjy.qa.enumtype.RunStatus;
import com.bjy.qa.enumtype.TaskStatus;
import com.bjy.qa.exception.MyException;
import com.bjy.qa.service.functionaltest.*;
import com.bjy.qa.util.TimeUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

import static com.bjy.qa.enumtype.GlobalParamScope.CASE_UNIQUENESS;
import static com.bjy.qa.enumtype.GlobalParamScope.SUIT_UNIQUENESS;

@Service
public class TestSuiteService extends ServiceImpl<TestSuiteDao, TestSuite> implements ITestSuiteService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    TestSuiteDao testSuiteDao;

    @Resource
    TestSuiteTestCaseDao testCaseTestSuiteDao;

    @Resource
    TestResultSuiteDao testResultSuiteDao;

    @Resource
    ITestResultCaseService iTestResultCaseService;

    @Resource
    TestResultCaseDao testResultCaseDao;

    @Resource
    TestSuiteAgentDao testSuiteAgentDao;

    @Resource
    TestCaseDao testCaseDao;

    @Resource
    StepDao stepDao;

    @Resource
    ApiDao apiDao;

    @Resource
    ITestSuiteAgentService iTestSuiteAgentService;

    @Resource
    ITestSuiteTestCaseService iTestSuiteTestCaseService;

    @Resource
    GlobalParamService globalParamService;

    @Resource
    IAgentService iAgentService;

    public static final String USER_PARAMETER_PREFIX = "CipherText:";

    @Override
    public int add(TestSuite testSuite) {
        testSuite.setStatus(TaskStatus.CREATE.getValue());
        int result = testSuiteDao.insert(testSuite);  //先插入一条数据，生成test suit id
        List<TestCase> testCases = testSuite.getTestCases();
        List<Agent> agents = testSuite.getAgents();

        Long suitId = testSuite.getId();
        // 保存testcase  testsuit映射关系
        if (testCases != null && testCases.size() != 0) {
            List<TestSuiteTestCase> testSuiteTestCaseList = new LinkedList<>();
            for (int i = 0; i < testCases.size(); i++) {
                TestSuiteTestCase testSuiteTestCase = new TestSuiteTestCase();
                testSuiteTestCase.setTestSuitesId(suitId);
                testSuiteTestCase.setTestCasesId(testCases.get(i).getId());
                testSuiteTestCaseList.add(testSuiteTestCase);
            }
            // 批量操作数据库
            iTestSuiteTestCaseService.saveBatch(testSuiteTestCaseList);
        }

        // 手工测试用例直接创建，测试结果表数据
        if (testSuite.getCaseType() == CatalogType.TEST_CASE) {
            TestResultSuite testResultSuite = new TestResultSuite();
            testResultSuite.setSuiteId(testSuite.getId());
            testResultSuite.setSuiteName(testSuite.getName());
            testResultSuite.setProjectId(testSuite.getProjectId());
            testResultSuite.setStatus(RunStatus.RUNNING.getValue());
            testResultSuite.setRunUser(testSuite.getOwner());
            testResultSuite.setCaseType(testSuite.getCaseType().getValue());
            testResultSuiteDao.insert(testResultSuite);
        }

        // 保存testsuit agents映射关系
        if (agents != null && agents.size() != 0) {
            List<TestSuiteAgent> testSuiteAgentList = new LinkedList<>();
            for (int i = 0; i < agents.size(); i++) {
                TestSuiteAgent testSuiteAgent = new TestSuiteAgent();
                testSuiteAgent.setTestSuitesId(suitId);
                testSuiteAgent.setAgentId(agents.get(i).getId());
                testSuiteAgentList.add(testSuiteAgent);
            }
            // 批量操作数据库
            iTestSuiteAgentService.saveBatch(testSuiteAgentList);
        }
        return result;
    }

    @Override
    public int update(TestSuite testSuite) {
        testSuite.setUpdatedAt(null);
        testSuite.setCreatedAt(null);

        if (testSuite.getRobotId() == null) {
            testSuite.setRobotId(0L);
        }

        List<TestCase> testCases = testSuite.getTestCases();
        List<Agent> agents = testSuite.getAgents();

        Long suiteId = testSuite.getId();
        // 批量更新 agents
        if (agents != null) {
            List<TestSuiteAgent> testSuiteAgentList = new ArrayList<>();

            for (int i = 0; i < agents.size(); i++) {
                TestSuiteAgent testSuiteAgent = new TestSuiteAgent();
                testSuiteAgent.setTestSuitesId(suiteId);
                testSuiteAgent.setAgentId(agents.get(i).getId());
                testSuiteAgentList.add(testSuiteAgent);
            }
            List<TestSuiteAgent> originAgentList = testSuiteAgentDao.selectByTestSuiteId(suiteId);

            if (originAgentList.size() == 0) {
                iTestSuiteAgentService.saveBatch(testSuiteAgentList);
            } else if (testSuiteAgentList.size() == 0) {
                iTestSuiteAgentService.removeBatchByIds(originAgentList);
            } else {
                iTestSuiteAgentService.removeBatchByIds(getNotIncludeList(originAgentList, testSuiteAgentList));
                iTestSuiteAgentService.saveBatch(getNotIncludeList(testSuiteAgentList, originAgentList));
            }
        }

        // 更新testcase
        if (testCases != null) {
            List<TestSuiteTestCase> testSuiteTestCasesList = new ArrayList<>();

            for (int i = 0; i < testCases.size(); i++) {
                TestSuiteTestCase testSuiteTestCase = new TestSuiteTestCase();
                testSuiteTestCase.setTestSuitesId(suiteId);
                testSuiteTestCase.setTestCasesId(testCases.get(i).getId());
                testSuiteTestCasesList.add(testSuiteTestCase);
            }
            List<TestSuiteTestCase> originTestSuiteTestCaseList = testCaseTestSuiteDao.selectByTestSuiteId(suiteId);

            if (originTestSuiteTestCaseList.size() == 0) {
                iTestSuiteTestCaseService.saveBatch(testSuiteTestCasesList);
            } else if (testSuiteTestCasesList.size() == 0) {
                iTestSuiteTestCaseService.removeBatchByIds(originTestSuiteTestCaseList);
            } else {
                iTestSuiteTestCaseService.removeBatchByIds(getNotIncludeList(originTestSuiteTestCaseList, testSuiteTestCasesList));
                iTestSuiteTestCaseService.saveBatch(getNotIncludeList(testSuiteTestCasesList, originTestSuiteTestCaseList));
            }

        }

        return testSuiteDao.updateById(testSuite);
    }

    @Override
    public List<TestSuite> selectAll(TestSuite testSuit) {
        QueryWrapper<TestSuite> queryWrapper = new QueryWrapper<>();
        if (testSuit.getProjectId() != null) {
            queryWrapper.eq("project_id", testSuit.getProjectId());
        }
        if (testSuit.getCaseType().getValue() != 0) {
            queryWrapper.eq("case_type", testSuit.getCaseType());
        }
        queryWrapper.orderByDesc("id");

        return testSuiteDao.selectList(queryWrapper);
    }

    @Override
    public ResponsePagingData list(MyPage<TestSuite> myPage) {
        Page<TestSuite> page = new Page(myPage.getPageNum(), myPage.getPageSize()); // 构造待查询 page 对象
        page.setOrders(OrderItem.descs("id")); // 按照id倒序排
        // 构造待查询 QueryWrapper
        TestSuite testSuite = myPage.getQuery();
        QueryWrapper<TestSuite> queryWrapper = new QueryWrapper<>();
        if (testSuite != null) {
            if (StringUtils.isNotBlank(testSuite.getProjectId().toString())) {
                queryWrapper.eq("project_id", testSuite.getProjectId());
            }
            if (testSuite.getCaseType().getValue() != 0) {
                queryWrapper.eq("case_type", testSuite.getCaseType());
            }
            if (StringUtils.isNotBlank(testSuite.getName())) {
                queryWrapper.like("name", testSuite.getName());
            }
        }
        page = testSuiteDao.selectPage(page, queryWrapper); // 按分页查询
        // 填充device信息
        List<TestSuite> pageRecordList = page.getRecords();
        for (int i = 0; i < pageRecordList.size(); i++) {
            List<Agent> agents = testSuiteAgentDao.findByTestSuiteId(pageRecordList.get(i).getId());
            pageRecordList.get(i).setAgents(agents);
        }

        myPage.setTotalRecords(page.getTotal()); // 设置返回总记录数

        return new ResponsePagingData(myPage, page.getRecords());
    }

    /**
     * 通过suite id来查找关联的case和agent
     *
     * @param suiteId
     * @return
     */
    @Override
    public TestSuite findById(Long suiteId) {
        TestSuite testSuite = testSuiteDao.selectById(suiteId);
        List<TestCase> testCases = testCaseTestSuiteDao.findByTestSuiteId(suiteId);
        List<Agent> agents = testSuiteAgentDao.findByTestSuiteId(suiteId);
        //装填 testcase和agent
        testSuite.setTestCases(testCases);
        testSuite.setAgents(agents);
        return testSuite;
    }

    @Override
    public int delete(Long id) {
        return testSuiteDao.deleteById(id);
    }

    @Override
    public TestResultSuite runSuite(Long suiteId, Long userId, String runUser) {
        TestSuite testSuite = testSuiteDao.selectById(suiteId); // 根据 suite id 查找 suite
        if (testSuite == null) {
            throw new MyException("Suite ID 不存在！");
        }

        List<Agent> agentsList = testSuiteAgentDao.findByTestSuiteId(suiteId); // 获取agent
        if (agentsList.size() == 0) {
            throw new MyException("没有关联Agent！！");
        }
        for (Agent agent : agentsList) {
            if (agent.getStatus() == AgentStatus.OFF_LINE) {
                throw new MyException("Agent 不在线，Agent name：" + agent.getName());
            }
        }

        List<TestCase> testCasesList = testCaseTestSuiteDao.findTestCaseAndStepByTestSuiteId(suiteId); // 根据 suite id 查找待运行的 test case 和 steps
        if (testCasesList.size() == 0) {
            throw new MyException("没有关联测试用例！");
        }

        return runSuite(testSuite, testCasesList, agentsList, userId, runUser);
    }

    @Override
    public TestResultSuite runSuite(TestSuite testSuite, List<TestCase> testCasesList, List<Agent> agentsList, Long userId, String runUser) {
        // 保存 suite 数据，创建result suite表
        TestResultSuite testResultSuite = new TestResultSuite();
        testResultSuite.setSuiteId(testSuite.getId());
        testResultSuite.setSuiteName(testSuite.getName());
        testResultSuite.setProjectId(testSuite.getProjectId());
        testResultSuite.setRunUser(runUser);
        // 置为running状态
        testResultSuite.setStatus(RunStatus.RUNNING.getValue());
        testResultSuiteDao.insert(testResultSuite);

        // 获取用例
        List<Long> pubList = new ArrayList<>(); // 公共步骤 id（由于公共步骤也是 test_case，所以这里是所有公共步骤的 testc_case id）
        Map<String, String> globalParamMap = new HashMap<>(); // 全局参数（环境参数 + 静态参数 + 用户参数 + 套件唯一参数）
        List<GlobalParam> caseUniqUniquenessParams = new ArrayList<>(); // 用例唯一参数列表
        int uniqueValue = this.getParamMap(testSuite.getProjectId(), testSuite.getEnv(), userId, globalParamMap, caseUniqUniquenessParams);

        // 把测试用例按照agent的数量平均分配
        List<List<TestCase>> caseForAgentList = averageTestCases(testCasesList, agentsList.size());
        for (int j = 0; j < caseForAgentList.size(); j++) {
            List<TestCaseDTO> testCasesDetailsList = new ArrayList<>();

            Long agent = agentsList.get(j).getId();
            List<TestCase> subCaseForAgentList = caseForAgentList.get(j);
            for (int i = 0; i < subCaseForAgentList.size(); i++) {
                TestResultCase testResultCase = new TestResultCase();
                testResultCase.setCid(subCaseForAgentList.get(i).getId());

                testResultCase.setAgent(agent);
                testResultCase.setRid(testResultSuite.getId());
                testResultCase.setStatus(RunStatus.RUNNING.getValue());
                testResultCaseDao.insert(testResultCase);

                TestCaseDTO testCaseDTO = new TestCaseDTO();

                testCaseDTO.setRid(testResultSuite.getId());
                testCaseDTO.setCid(subCaseForAgentList.get(i).getId());

                TestCase testCase = subCaseForAgentList.get(i);
                testCaseDTO.setName(testCase.getName());
                testCaseDTO.setDataProvider(testCase.getDataProvider());

                // 产生用例唯一参数，并设置全局变量
                for (GlobalParam globalParam : caseUniqUniquenessParams) {
                    globalParamMap.put(globalParam.getParamName(), TimeUtil.generateTimestampParameter(globalParam.getLength(), uniqueValue++));
                }
                testCaseDTO.setGp((Map<String, String>) ((HashMap<String, String>) globalParamMap).clone());

                List<StepDTO> stepList = new ArrayList<>();
                // 根据case id查找step
                List<Step> steps = subCaseForAgentList.get(i).getSteps();
                for (Step step : steps) {
                    if (step.getStepType() == CatalogType.PUBLIC_STEP) { // 公共步骤，将 id 放入 pubList
                        pubList.add(step.getApiId());
                    } else { // 非公共步骤，根据 api id 组装 api
                        Api api = apiDao.findById(step.getApiId());
                        step.setApi(api);
                    }
                    StepDTO stepDTO = new StepDTO();
                    stepDTO.setStep(step);
                    stepList.add(stepDTO);
                }
                testCaseDTO.setSteps(stepList);
                testCasesDetailsList.add(testCaseDTO);
            }
            logger.info("testCasesDetailsList: {}: ", testCasesDetailsList);

            RunTestSuite runTestSuite = new RunTestSuite();
            runTestSuite.setCaseType(testSuite.getCaseType().getValue());
            runTestSuite.setMsg("suite");
            runTestSuite.setCases(testCasesDetailsList);

            // 组装公共步骤
            List<TestCaseDTO> pubTestCasesDetailsList = new ArrayList<>();
            for (Long testCaseId : pubList) {
                TestCase testCase = testCaseDao.findById(testCaseId);

                TestCaseDTO testCaseDTO = new TestCaseDTO();
                testCaseDTO.setName(testCase.getName());
                testCaseDTO.setCid(testCaseId);

                List<StepDTO> stepList = new ArrayList<>();
                // 根据case id查找step
                List<Step> steps = stepDao.findByTestCaseId(testCaseId);
                for (Step step : steps) {
                    Api api = apiDao.findById(step.getApiId());
                    step.setApi(api);
                    StepDTO stepDTO = new StepDTO();
                    stepDTO.setStep(step);
                    stepList.add(stepDTO);
                }
                testCaseDTO.setSteps(stepList);

                pubTestCasesDetailsList.add(testCaseDTO);
            }
            runTestSuite.setPubCases(pubTestCasesDetailsList); // 将公共步骤，加入 TestSuite

            iAgentService.pubCmd(agentsList.get(j).getKey(), "runPubCmd", JSON.toJSONString(runTestSuite)); // 下发套件
        }
        return testResultSuite;
    }

    @Override
    public boolean forceStopSuite(Long id, String runUser) {
        // 查询测试结果数据
        TestResultSuite testResultSuite = testResultSuiteDao.selectById(id); // 根据 suite id 查找 suite
        if (testResultSuite == null) {
            throw new MyException("测试结果停止失败：测试结果 ID: " + id + " 不存在！");
        }
        Long suiteId = testResultSuite.getSuiteId();
        if (suiteId == -9999L) {
            throw new MyException("测试结果停止失败：无法停止 DEBUG 测试结果！");
        }

        // 查询测试套件
        TestSuite testSuite = testSuiteDao.selectById(suiteId); // 根据 suite id 查找 suite
        if (testSuite == null) {
            throw new MyException("测试结果停止失败：Suite ID: " + suiteId + " 不存在或已删除！");
        }

        // 查询测试套件对应的 agent 列表
        List<Agent> agents = testSuiteAgentDao.findByTestSuiteId(suiteId); // 获取agent
        List<Agent> agentList = new ArrayList<>();
        for (Agent agent : agents) {
            if (agent.getStatus() != AgentStatus.OFF_LINE) {
                agentList.add(agent);
            }
        }
        if (agentList.size() == 0) {
            throw new MyException("测试结果停止失败：测试套件未关联 Agent 或关联的 Agent 不在线！");
        }

        // 查询测试用例
        List<TestCase> testCasesList = testCaseTestSuiteDao.findTestCaseAndStepByTestSuiteId(suiteId); // 根据 suite id 查找待运行的 test case 和 steps
        if (testCasesList.size() == 0) {
            throw new MyException("测试结果停止失败：测试套件未关联用例，无须停止！！");
        }

        // 更新测试结果为 - 中断
        testResultSuite.setStatus(RunStatus.BROKEN.getValue());
        testResultSuite.setRunUser(runUser);
        testResultSuite.setEndAt(new Date());
        testResultSuite.setTotalTime(TimeUtil.getTimeDiff(testResultSuite.getCreatedAt(), new Date()));
        testResultSuiteDao.updateById(testResultSuite);

        // 更新测试结果关联测试用例中未运行完成的为 - 中断
        QueryWrapper<TestResultCase> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("rid", testResultSuite.getId());
        queryWrapper.eq("status", RunStatus.RUNNING.getValue());
        List<TestResultCase> testResultCaseList = testResultCaseDao.selectList(queryWrapper);
        for (TestResultCase temp : testResultCaseList) {
            temp.setStatus(RunStatus.BROKEN.getValue());
        }
        iTestResultCaseService.updateBatchById(testResultCaseList);

        // 组装下发数据
        List<JSONObject> suiteDetail = new ArrayList<>();
        for (TestCase testCase : testCasesList) {
            JSONObject suite = new JSONObject();
            suite.put("cid", testCase.getId());
            suite.put("rid", id);
            suiteDetail.add(suite);
        }
        JSONObject result = new JSONObject();
        result.put("msg", "forceStopSuite");
        result.put("type", CatalogType.INTERFACE_TEST_CASE.getValue());
        result.put("cases", suiteDetail);

        // 循环下发给 agent
        for (Agent agent : agentList) {
            iAgentService.pubCmd(agent.getKey(), "forceStopPubCmd", JSON.toJSONString(result)); // 下发
        }

        return true;
    }

    @Override
    public int getParamMap(Long projectID, String env, Long userId, Map<String, String> globalParamMap, List<GlobalParam> caseUniqUniquenessParams) {
        int uniqueValue = 0;

        GlobalParamDTO globalParamDTO = globalParamService.getAllGlobalParam(projectID, env, userId); // 从数据库中查询全局参数
        if (globalParamDTO.getEnvParams() != null) {
            for (EnvParam envParam : globalParamDTO.getEnvParams()) {
                globalParamMap.put(envParam.getServerName(), envParam.getValue());
            }
        }

        if (globalParamDTO.getGlobalParams() != null) {
            for (GlobalParam globalParam : globalParamDTO.getGlobalParams()) {
                switch (globalParam.getGlobalParamType()) {
                    case STATIC_PARAMETER:
                        globalParamMap.put(globalParam.getParamName(), globalParam.getValue());
                        break;
                    case DYNAMIC_PARAMETER:
                        if (globalParam.getGlobalParamScope().equals(CASE_UNIQUENESS)) { // case 唯一
                            caseUniqUniquenessParams.add(globalParam);
                        } else if (globalParam.getGlobalParamScope().equals(SUIT_UNIQUENESS)) { // 套件唯一
                            globalParamMap.put(globalParam.getParamName(), TimeUtil.generateTimestampParameter(globalParam.getLength(), uniqueValue++));
                        }
                        break;
                    case USER_PARAMETER:
                        globalParamMap.put(globalParam.getParamName(), USER_PARAMETER_PREFIX + globalParam.getValue()); // 用户参数，前面加前缀
                        break;
                }
            }
        }
        return uniqueValue;
    }

    @Override
    public TestResultSuite updateReserved(Long id) {
        TestResultSuite testResultSuite = testResultSuiteDao.selectById(id);
        if (testResultSuite.getReserved() != null && testResultSuite.getReserved()) {
            testResultSuite.setReserved(false);
        } else {
            testResultSuite.setReserved(true);
        }
        testResultSuiteDao.updateById(testResultSuite);
        return testResultSuite;
    }

    /*
     *  遍历 originList,将不在 comparedList 中的元素添加到 notIncludeList
     */
    private <T> List getNotIncludeList(List<T> originList, List<T> comparedList) {
        List<T> notIncludeList = new ArrayList<>();
        for (int i = 0; i < originList.size(); i++) {
            if (!comparedList.contains(originList.get(i))) {
                notIncludeList.add(originList.get(i));
            }
        }
        return notIncludeList;
    }

    /*
     * 把List分为N等份
     */
    private <T> List<List<T>> averageTestCases(List<T> lst, int n) {
        List<List<T>> result = new ArrayList<List<T>>();
        int size = lst.size();
        int partSize = size / n;
        int remain = size % n;
        int offset = 0;

        for (int i = 0; i < n; i++) {
            List<T> value = null;
            if (remain > 0) {
                value = lst.subList(i * partSize + offset, (i + 1) * partSize + offset + 1);
                remain--;
                offset++;
            } else {
                value = lst.subList(i * partSize + offset, (i + 1) * partSize + offset);
            }
            result.add(value);
        }
        return result;
    }
}
